package com.zhengx.blehelper;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.util.Log;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;


/**
 * name：BleManager
 * class: 蓝牙管理器
 * author: zhengx
 * create_time: 2018/10/29
 */

public class BleManager implements BleCallback.OnScanStateChangedCallback{

    private static final String TAG = BleManager.class.getSimpleName();

    private boolean logOpen = true;

    private Context context;

    private Handler handler = new Handler();

    private List<String> nameFilter = new ArrayList<>();

    private BleScanner bleScanner;

    private int scanTimeOut = 12 * 1000;

    /**
     * 重连间隔
     */
    private int reconnectIntervalSec = 2;

    private String HEART_BEAT = "h-b\r\n";

    private long heartBeatIntervalMillsec = 5 * 1000;

    private UUID UUID_SERVICE = null;
    private UUID uuid_notify = null;
    private UUID uuid_write = null;

    private MessageDecoder notifyMessageDecoder;

    private MessageDecoder readMessageDecoder;

    private BluetoothAdapter mBluetoothAdapter;

    private List<BleCallback.OnBleClientChangeListener> listenerList = new ArrayList<>();

    private BleCallback.OnBluetoothStatusListener onBluetoothStatusListener;

    private List<BluetoothLeClient> clients = new ArrayList<>();

    private Map<String, BluetoothLeClient> needReConnectDevices = new HashMap<>();

    public void addOnBleClientChangeListener(BleCallback.OnBleClientChangeListener listener) {
        if (!listenerList.contains(listener)) {
            listenerList.add(listener);
        }
    }

    public void removeOnBleClientChangeListener(BleCallback.OnBleClientChangeListener listener) {
        listenerList.remove(listener);
    }

    public void removeAllListener() {
        listenerList.clear();
    }

    public BleManager setOnBluetoothStatusListener(BleCallback.OnBluetoothStatusListener onBluetoothStatusListener) {
        this.onBluetoothStatusListener = onBluetoothStatusListener;
        return this;
    }

    private BleManager() {
    }

    private static class Singleton {
        private final static BleManager instance = new BleManager();
    }

    public static BleManager getInstance(){
        if (Singleton.instance.context == null) {
            throw new IllegalArgumentException("BleManager使用前需要在application中初始化");
        }
        return Singleton.instance;
    }

    public static BleManager init(Context context) {
        if (Singleton.instance.context == null) {
            Singleton.instance.context = context.getApplicationContext();
            Singleton.instance.registerBroadcast(context.getApplicationContext());
        }
        return Singleton.instance;
    }

    public boolean apply() {

        BluetoothManager mBluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
        if (mBluetoothManager == null) {
            if (logOpen) {
                Log.e(TAG, "Unable to initialize BluetoothManager.");
            }
            return false;
        }

        mBluetoothAdapter = mBluetoothManager.getAdapter();
        if (mBluetoothAdapter == null) {
            if (logOpen) {
                Log.e(TAG, "无法获取蓝牙适配器");
            }
            return false;
        }
        bleScanner = new BleScanner()
                .with(context, mBluetoothAdapter)
                .log(logOpen)
                .timeOut(scanTimeOut)
                .withNameFilter(nameFilter);
        return true;
    }

    /**
     * 日志开关
     * @param logOpen
     * @return
     */
    public BleManager logOpen(boolean logOpen) {
        this.logOpen = logOpen;
        return this;
    }

    /**
     * 客户端心跳设置
     * @param heartbeat
     * @param heartBeatIntervalMillsec
     */
    public void setHeartbeat(String heartbeat, int heartBeatIntervalMillsec) {
        this.HEART_BEAT = heartbeat;
        this.heartBeatIntervalMillsec = heartBeatIntervalMillsec;
    }

    /**
     * 设置扫描超时，最长12s，默认12s
     * @param timeoutMillsec
     * @return
     */
    public BleManager setScanTimeout(int timeoutMillsec) {
        if (timeoutMillsec >= 12000) {
            this.scanTimeOut = 12000;
        } else if (timeoutMillsec <= 0) {
            throw new IllegalArgumentException("超时时间设置错误");
        } else {
            this.scanTimeOut = timeoutMillsec;
        }
        return this;
    }

    /**
     * 增加设备的名称过滤
     * @param name
     * @return
     */
    public BleManager addNameFilter(String name) {
        this.nameFilter.add(name);
        return this;
    }

    /**
     * 重置名称过滤
     * @return
     */
    public BleManager resetNameFilter() {
        this.nameFilter = new ArrayList<>();
        return this;
    }

    public BleManager setDefaultCharacteristicsUUID(UUID uuidService, UUID uuid_notify, UUID uuid_write) {
        this.UUID_SERVICE = uuidService;
        this.uuid_notify = uuid_notify;
        this.uuid_write = uuid_write;
        return this;
    }

    public BleManager setNotifyMessageDecoder(MessageDecoder notifyMessageDecoder) {
        this.notifyMessageDecoder = notifyMessageDecoder;
        return this;
    }

    public BleManager setReadMessageDecoder(MessageDecoder readMessageDecoder) {
        this.readMessageDecoder = readMessageDecoder;
        return this;
    }

    public boolean isSupportBle() {
        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
    }

    public void enableBluetoothAutoReEnable() {

        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (!isBluetoothEnable()) {
                    enableBluetoothAutoReEnable();
                }
            }
        }, 10 * 1000);
        enableBluetooth();
    }

    /**
     * 打开蓝牙
     */
    public void enableBluetooth() {
        if (logOpen) {
            Log.i(TAG, "请求开启蓝牙");
        }
        mBluetoothAdapter.enable();
    }

    /**
     * closeBluetoothGatt bluetooth
     */
    public void disableBluetooth() {
        if (logOpen) {
            Log.i(TAG, "关闭蓝牙");
        }
        mBluetoothAdapter.disable();
    }

    /**
     * is bluetooth enable?
     * @return
     */
    public boolean isBluetoothEnable() {
        return mBluetoothAdapter.isEnabled();
    }

    /**
     * 关闭连接
     */
    public void closeConnect(BluetoothLeClient bluetoothLeClient) {
        bluetoothLeClient.closeConnect();
    }

    /**
     * 判断是否正在扫描
     * @return
     */
    public boolean isScanning() {
        if (bleScanner != null) {
            return bleScanner.isScanning();
        }
        return false;
    }

    /**
     * 连接到指定设备
     * @param device
     */
    public BluetoothLeClient newBleClient(BluetoothDevice device) {
        return newBleClient(true, device);
    }

    public BluetoothLeClient newBleClient(boolean shouldAutoReconnect, BluetoothDevice device) {
        for (BluetoothLeClient client : clients) {
            if (client.getClientMac().equals(device.getAddress())) {
                return client;
            }
        }
        cancelScan();
        BluetoothLeClient bluetoothLeClient = new BluetoothLeClient(context, device);
        bluetoothLeClient.setGattCallback(bleGattCallback)
                .log(logOpen)
                .setShouldReconnect(shouldAutoReconnect)
                .setDefaultCharacteristicsUUID(UUID_SERVICE, uuid_notify, uuid_write)
                .setHeartBeat(HEART_BEAT, heartBeatIntervalMillsec);

        if (notifyMessageDecoder != null) {
            bluetoothLeClient.setNotifyMessageDecoder(notifyMessageDecoder);
        }
        if (readMessageDecoder != null) {
            bluetoothLeClient.setReadMessageDecoder(readMessageDecoder);
        }
        clients.add(bluetoothLeClient);
        return bluetoothLeClient;
    }

    /**
     * 扫描蓝牙设备
     * @return
     */
    public boolean scanDevices(BleCallback.OnScanStateChangedCallback onScanStateChangedCallback) {
        return scanDevices(null, onScanStateChangedCallback);
    }

    /**
     * 扫描指定蓝牙设备
     * @param mac
     * @return
     */
    public boolean scanDevices(String mac, BleCallback.OnScanStateChangedCallback onScanStateChangedCallback) {

        if (bleScanner != null && bleScanner.isScanning()) {
            return false;
        }
        bleScanner.setScanCallback(onScanStateChangedCallback);

        return bleScanner.scan(mac);
    }

    /**
     * 重连扫描蓝牙设备
     * 扫描蓝牙设备
     * @return
     */
    private boolean scanDevices() {

        if (bleScanner != null && bleScanner.isScanning()) {
            return false;
        }

        bleScanner.setScanCallback(this);

        return bleScanner.scan();
    }

    /**
     * 取消蓝牙扫描
     */
    public void cancelScan() {
        if (bleScanner != null && bleScanner.isScanning()) {
            bleScanner.cancelScan();
        }
    }

    /**
     * 注意释放资源
     */
    public void release() {
        for (BluetoothLeClient client : clients) {
            closeConnect(client);
        }
        unRegisterBroadcast(context);
        removeAllListener();
        onBluetoothStatusListener = null;
    }

    /*==================================扫描回调=================================================*/
    @Override
    public void onStartScan() {
        if (logOpen) {
            Log.i(TAG, "重连扫描：需要重连的设备：" + needReConnectDevices);
        }
        if (needReConnectDevices.size() > 0) {
            for (Map.Entry<String, BluetoothLeClient> stringBluetoothLeClientEntry :
                    needReConnectDevices.entrySet()) {

                stringBluetoothLeClientEntry.getValue().reconnecting();
            }
        }
    }

    @Override
    public void onDiscoveryDevice(BluetoothDevice bluetoothDevice) {
        if (needReConnectDevices.size() == 1) {
            BluetoothLeClient tempClient = needReConnectDevices.entrySet().iterator().next().getValue();
            if (bluetoothDevice.getAddress()
                    .equals(tempClient.getClientMac())) {

                cancelScan();
                tempClient.reConnect(bluetoothDevice);
            }
        }
    }

    @Override
    public void onFindSpecifiedDevice(BluetoothDevice bluetoothDevice) {

    }

    @Override
    public void onFinished(boolean isCanceled, List<BluetoothDevice> devices) {
        if (logOpen) {
            Log.i(TAG, "重连扫描结束:" + devices);
        }
        if (!isCanceled) {
            if (needReConnectDevices.size() > 0) {
                for (Map.Entry<String, BluetoothLeClient> stringBluetoothLeClientEntry
                        : needReConnectDevices.entrySet()) {

                    for (BluetoothDevice device : devices) {
                        if (stringBluetoothLeClientEntry.getValue().getClientMac()
                                .equals(device.getAddress())) {

                            stringBluetoothLeClientEntry.getValue().reConnect(device);
                            break;
                        }
                    }
                }

                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (needReConnectDevices.size() > 0) {
                            scanDevices();
                        }
                    }
                }, reconnectIntervalSec * 1000);
            }
        }
    }

    @Override
    public void onTimeOut() {

    }

    /**
     * 蓝牙设备状态回调
     */
    private BleCallback.BleGattCallback bleGattCallback = new BleCallback.BleGattCallback() {
        @Override
        public void onConnected(final BluetoothLeClient client) {
            if (logOpen) {
                Log.i(TAG, "蓝牙设备连接成功：" + client.getClientMac());
            }
            needReConnectDevices.remove(client.getClientMac());

            handler.post(new Runnable() {
                @Override
                public void run() {
                    for (BleCallback.OnBleClientChangeListener onBleClientChangeListener : listenerList) {
                        onBleClientChangeListener.onConnected(client);
                    }
                }
            });

        }

        @Override
        public void onConnectTimeout(final BluetoothLeClient client) {
            if (logOpen) {
                Log.i(TAG, "设备连接超时");
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    for (BleCallback.OnBleClientChangeListener onBleClientChangeListener : listenerList) {
                        onBleClientChangeListener.onConnectTimeout(client);
                    }
                }
            });

        }

        @Override
        public void onDisconnected(final BluetoothLeClient client) {
            if (logOpen) {
                Log.i(TAG, "蓝牙设备断开：" + client.getClientMac());
            }
            if (client.isShouldReConnect()) {
                needReConnectDevices.put(client.getClientMac(), client);
                if (isBluetoothEnable()) {
                    handler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            if (needReConnectDevices.size() > 0) {
                                scanDevices();
                            }
                        }
                    }, reconnectIntervalSec * 1000);
                }

            } else {
                clients.remove(client);
            }

            handler.post(new Runnable() {
                @Override
                public void run() {
                    for (BleCallback.OnBleClientChangeListener onBleClientChangeListener : listenerList) {
                        onBleClientChangeListener.onDisconnected(client);
                    }
                }
            });
        }

        @Override
        public void onReConnect(final BluetoothLeClient client, final int count) {
            if (logOpen) {
                Log.i(TAG, "设备重连：" + client.getClientMac() + "  重连次数：" + count);
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    for (BleCallback.OnBleClientChangeListener onBleClientChangeListener : listenerList) {
                        onBleClientChangeListener.onReConnect(client, count);
                    }
                }
            });
        }

        @Override
        public void onServiceDiscovered(final BluetoothLeClient client, final List<BluetoothGattService> services) {
            if (logOpen) {
                Log.i(TAG, "蓝牙设备服务：" + services);
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    for (BleCallback.OnBleClientChangeListener onBleClientChangeListener : listenerList) {
                        onBleClientChangeListener.onServiceDiscovered(client, services);
                    }
                }
            });
        }


        @Override
        public void onGattCharacteristicSet(final BluetoothLeClient client) {
            if (logOpen) {
                Log.i(TAG, "设置通信特征值:" + client.getClientMac());
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    for (BleCallback.OnBleClientChangeListener onBleClientChangeListener : listenerList) {
                        onBleClientChangeListener.onGattCharacteristicSet(client);
                    }
                }
            });

        }

        @Override
        public void onWriteValue(final BluetoothLeClient client, final byte[] writeData) {
            if (logOpen) {
                Log.i(TAG, client.getClientMac() + "发出的数据："
                        + new String(writeData));
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    for (BleCallback.OnBleClientChangeListener onBleClientChangeListener : listenerList) {
                        onBleClientChangeListener.onWriteValue(client, writeData);
                    }
                }
            });

        }

        @Override
        public void onRead(final BluetoothLeClient client, final BluetoothLeClient.MsgType msgType, final byte[] source) {
            if (logOpen) {
                Log.i(TAG, client.getClientMac() + msgType
                        + " 获取数据：" + new String(source) + "\n"
                        + " 16进制：" + HexUtil.encodeHexStr(source)
                );
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    for (BleCallback.OnBleClientChangeListener onBleClientChangeListener : listenerList) {
                        onBleClientChangeListener.onRead(client, msgType, source);
                    }
                }
            });
        }

        @Override
        public void onReadRemoteRssi(final BluetoothLeClient client, final int rssi, final boolean success) {
            if (logOpen) {
                Log.i(TAG, client.getClientMac() + " 读取到的rssi："
                        + rssi + "  " + success);
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    for (BleCallback.OnBleClientChangeListener onBleClientChangeListener : listenerList) {
                        onBleClientChangeListener.onReadRemoteRssi(client, rssi, success);
                    }
                }
            });
        }
    };


    private void registerBroadcast(Context context) {
        IntentFilter intentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
        context.registerReceiver(broadcastReceiver, intentFilter);
    }

    private void unRegisterBroadcast(Context context) {
        context.unregisterReceiver(broadcastReceiver);
    }

    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
                /*系统广播，监听蓝牙开启或关闭*/
                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
                        BluetoothAdapter.ERROR);
                switch (state) {
                    case BluetoothAdapter.STATE_OFF:
                        if (logOpen) {
                            Log.i(TAG, "蓝牙关闭");
                        }
                        if (onBluetoothStatusListener != null) {
                            onBluetoothStatusListener.onStatusChange(false);
                        }
                        if (needReConnectDevices.size() > 0) {
                            enableBluetoothAutoReEnable();
                        }
                        break;
                    case BluetoothAdapter.STATE_TURNING_OFF:

                        break;
                    case BluetoothAdapter.STATE_ON:
                        if (logOpen) {
                            Log.i(TAG, "蓝牙开启");
                        }
                        if (onBluetoothStatusListener != null) {
                            onBluetoothStatusListener.onStatusChange(true);
                        }

                        if (needReConnectDevices.size() > 0) {
                            scanDevices();
                        }
                        break;
                    case BluetoothAdapter.STATE_TURNING_ON:

                        break;
                }
            }
        }
    };

    public List<BluetoothLeClient> getClients() {
        return clients;
    }
}
